package class02;

public class Code02_RandToRand {

  // 此函数只能用，不能修改
  // 等概率返回1~5
  public static int f() {
    return (int) (Math.random() * 5) + 1;
  }

  // 等概率得到0和1
  public static int a() {
    int ans = 0;
    do {
      ans = f();
    } while (ans == 3);
    return ans < 3 ? 0 : 1;
  }

  // 等概率返回0~6
  public static int b() {
    int ans = 0;
    do {
      ans = (a() << 2) + (a() << 1) + a();
    } while (ans == 7);
    return ans;
  }

  // 等概率返回1~7
  public static int c() {
    return b() + 1;
  }

  // 这个结构是唯一的随机机制
  // 你只能初始化并使用，不可修改
  public static class RandomBox {
    private final int min;
    private final int max;

    // 初始化时请一定不要让mi==ma
    public RandomBox(int mi, int ma) {
      min = mi;
      max = ma;
    }

    // 13 ~ 17
    // 13 + [0,4]
    public int random() {
      return min + (int) (Math.random() * (max - min + 1));
    }

    public int min() {
      return min;
    }

    public int max() {
      return max;
    }
  }

  // 利用条件RandomBox，如何等概率返回0和1
  public static int rand01(RandomBox randomBox) {
    int min = randomBox.min();
    int max = randomBox.max();
    // min ~ max
    int size = max - min + 1;
    // size是不是奇数，odd 奇数
    boolean odd = (size & 1) != 0;
    int mid = size / 2;
    int ans = 0;
    do {
      ans = randomBox.random() - min;
    } while (odd && ans == mid);
    return ans < mid ? 0 : 1;
  }

  // 给你一个RandomBox，这是唯一能借助的随机机制
  // 等概率返回from~to范围上任何一个数
  // 要求from<=to
  public static int random(RandomBox randomBox, int from, int to) {
    if (from == to) {
      return from;
    }
    // 3 ~ 9
    // 0 ~ 6
    // 0 ~ range
    int range = to - from;
    int num = 1;
    // 求0～range需要几个2进制位
    while ((1 << num) - 1 < range) {
      num++;
    }

    // 我们一共需要num位
    // 最终的累加和，首先+0位上是1还是0，1位上是1还是0，2位上是1还是0...
    int ans = 0;
    do {
      ans = 0;
      for (int i = 0; i < num; i++) {
        ans |= (rand01(randomBox) << i);
      }
    } while (ans > range);
    return ans + from;
  }

  public static void main(String[] args) {
    System.out.println("=========random等概率测试开始=========");
    // Math.random() -> double -> [0,1)

    int testTimes = 10000000;
    int count = 0;
    for (int i = 0; i < testTimes; i++) {
      // Math.random 等概率返回数据
      if (Math.random() < 0.75) {
        count++;
      }
    }
    // 测试是否等概率返回
    System.out.println((double) count / (double) testTimes);

    System.out.println();
    System.out.println("=========random等概率测试开始2=========");

    // 0.00 - 0.99 乘以8, 值域为 [0, 8), 共8段[0,1] [1,2]...
    // [0,1) -> [0,8)
    count = 0;
    for (int i = 0; i < testTimes; i++) {
      if (Math.random() * 8 < 5) {
        count++;
      }
    }
    System.out.println((double) count / (double) testTimes);
    System.out.println((double) 5 / (double) 8);

    System.out.println();
    System.out.println("=========random等概率测试开始3=========");

    int K = 9;
    // [0,K) -> [0,8]

    int[] counts = new int[9];
    for (int i = 0; i < testTimes; i++) {
      int ans = (int) (Math.random() * K); // [0,K-1]
      counts[ans]++;
    }
    for (int i = 0; i < K; i++) {
      System.out.println(i + "这个数，出现了 " + counts[i] + " 次");
    }

    System.out.println();
    System.out.println("=========random 概率由x调整为1 - (1-x)^2=========");

    count = 0;
    double x = 0.17;
    for (int i = 0; i < testTimes; i++) {
      if (xToXPower3() < x) {
        count++;
      }
    }
    // 比 0.17 小的概率
    System.out.println((double) count / (double) testTimes);
    // 1 - (1 - x)^2
    System.out.println((double) 1 - Math.pow((double) 1 - x, 2));

    // 提供一个 1-5 等概率函数, 需要改造为 1-7 等概率函数
    System.out.println();
    System.out.println("=========random 将1-5等概率随机函数改造为1-0等概率函数=========");
    count = 0;
    for (int i = 0; i < testTimes; i++) {
      if (f2() == 0) {
        count++;
      }
    }
    System.out.println((double) count / (double) testTimes);

    System.out.println();
    System.out.println("=========random 将1-0等概率随机函数改造为1-7等概率函数=========");

    counts = new int[8];
    for (int i = 0; i < testTimes; i++) {
      int num = g();
      counts[num]++;
    }
    for (int i = 0; i < 8; i++) {
      System.out.println(i + "这个数，出现了 " + counts[i] + " 次");
    }
  }

  // 返回[0,1)的一个小数
  // 任意的x，x属于[0,1)，[0,x)范围上的数出现概率由原来的x调整成x平方
  public static double xToXPower2() {
    // 命中 0-0.3 概率由一次random 变成两次, 即需要第一个random满足 且 第二个random满足
    // 那么就是 0.3 * 0.3 -> x * x = x^2
    return Math.max(Math.random(), Math.random());
    //  x * x * X = x^3
    // return Math.max(Math.random(), Math.random(), Math.random());
  }

  public static double xToXPower3() {
    // 两次均得不到 0-x 范围内的数据, (1-X) * (1-X), 两个值均不在
    // 一个在则不成立
    return Math.min(Math.random(), Math.random());
  }

  // lib里的，不能改！
  // 等概率返回 1-5
  public static int f1() {
    return (int) (Math.random() * 5) + 1;
  }

  // 随机机制，只能用f1，
  // 等概率返回0和1
  public static int f2() {
    // 1-2 返回 0
    // 3   继续循环
    // 4-5 返回 1
    int ans = 0;
    do {
      ans = f1();
    } while (ans == 3);
    return ans < 3 ? 0 : 1;
  }

  // 得到000 ~ 111 做到等概率 0 ~ 7等概率返回一个
  // 1-7 共7个数, 需要 3 个比特位, 即: f1 f1 f1
  // 000 概率 = 1/8
  // 001 概率 = 1/8
  // ...
  // 111 概率 = 1/8
  public static int f3() {
    // << 2 表示第3个比特位
    // << 1 表示第2个比特位
    // << 0 表示第1个比特位
    // 即 2^3 = 8 个数, 最大值为 7
    return (f2() << 2) + (f2() << 1) + (f2() << 0);
  }

  // 0 ~ 6等概率返回一个
  public static int f4() {
    int ans = 0;
    do {
      ans = f3();
    } while (ans == 7);
    return ans;
  }

  public static int g() {
    return f4() + 1;
  }

  // 你只能知道，x会以固定概率返回0和1，但是x的内容，你看不到！
  public static int x() {
    return Math.random() < 0.84 ? 0 : 1;
  }

  // 等概率返回0和1
  // 思路: 0的概率是 p , 1的概率是q
  // 那么:
  // 00 p*p 继续循环
  // 01 p*q 返回0
  // 10 q*p 返回1
  // 11 q*q 继续循环
  public static int y() {
    int ans = 0;
    do {
      ans = x();
    } while (ans == x());
    return ans;
  }
}
